namespace SimpleWebServices.Net.Mail
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.IO;
    using System.Net.Mail;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.UI;
    using System.Xml.XPath;
    using SimpleWebServices;
    using SimpleWebServices.Configuration;
    using SimpleWebServices.IO;
    using SimpleWebServices.Models;
    using SimpleWebServices.Reflection;

    public sealed class InternalServerErrorMailMessage : MailMessage
    {
        private ExceptionContext _context;

        public InternalServerErrorMailMessage(ExceptionContext context)
        {
            this.Context = context;
        }

        public ExceptionContext Context
        {
            get
            {
                return this._context;
            }

            set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }

                this._context = value;
            }
        }

        private AlternateView TextPlainView
        {
            get
            {
                StringBuilder buffer = new StringBuilder();
                buffer.AppendLine(DateTimeFactory.UtcNow.ToString("r", CultureInfo.InvariantCulture));
                buffer.AppendLine(string.Empty);

                Exception exception = this.Context.Exception;
                buffer.AppendLine("Exception:");
                buffer.AppendLine(exception.Message);

                return AlternateView.CreateAlternateViewFromString(buffer.ToString(), null, "text/plain");
            }
        }

        private AlternateView TextHtmlView
        {
            get
            {
                StringBuilder buffer = new StringBuilder();
                using (StringWriter writer = new StringWriter(buffer, CultureInfo.InvariantCulture))
                {
                    using (HtmlTextWriter html = new HtmlTextWriter(writer, string.Empty))
                    {
                        html.RenderBeginTag(HtmlTextWriterTag.P);
                        html.WriteEncodedText(DateTimeFactory.UtcNow.ToString("r", CultureInfo.InvariantCulture));
                        html.RenderEndTag();

                        html.RenderBeginTag(HtmlTextWriterTag.H2);
                        html.WriteEncodedText("Exception");
                        html.RenderEndTag();

                        Exception exception = this.Context.Exception;
                        html.RenderBeginTag(HtmlTextWriterTag.P);
                        html.WriteEncodedText(exception.Message);
                        html.RenderEndTag();
                    }
                }

                return AlternateView.CreateAlternateViewFromString(buffer.ToString(), null, "text/html");
            }
        }

        public void Send()
        {
            InternalServerErrorSettings settings = InternalServerErrorConfiguration.Settings();
            if (null == settings && null != settings.Notification && 0 != settings.Notification.To.Count)
            {
                /// TODO : Write to event log instead
            }
            else
            {
                HttpRequestBase request = this.Context.HttpContext.Request;
                this.Subject = string.Concat(request.HttpMethod, " ", request.Url.AbsoluteUri, " (500 Internal Server Error)");

                this.From = settings.Notification.From.ToMailAddress();
                foreach (Email email in settings.Notification.To)
                {
                    this.To.Add(email.ToMailAddress());
                }

                this.IsBodyHtml = true;
                this.AlternateViews.Add(this.TextPlainView);
                this.AlternateViews.Add(this.TextHtmlView);

                using (HttpRequestBaseFile requestFile = new HttpRequestBaseFile(request))
                using (FileStream requestStream = requestFile.Open())
                using (TextAttachment requestAttachment = new TextAttachment(requestStream, "request.txt"))
                {
                    this.Attachments.Add(requestAttachment);

                    using (SerializationFile exceptionFile = new SerializationFile(this.Context.Exception))
                    using (FileStream exceptionStream = exceptionFile.Open())
                    using (XmlAttachment exceptionAttachment = new XmlAttachment(exceptionStream, "exception.xml"))
                    {
                        this.Attachments.Add(exceptionAttachment);

                        using (SerializationFile contextFile = new SerializationFile(ToXml(this.Context)))
                        using (FileStream contextStream = contextFile.Open())
                        using (XmlAttachment contextAttachment = new XmlAttachment(contextStream, "context.xml"))
                        {
                            this.Attachments.Add(contextAttachment);

                            new SmtpClient().Send(this);
                        }
                    }
                }
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "No exceptions should leak out here.")]
        private static IXPathNavigable ToXml(ExceptionContext context)
        {
            IXPathNavigable result = null;

            try
            {
                result = context.ToXml();
            }
            catch (Exception exception)
            {
                result = exception.Serialize();
            }

            return result;
        }
    }
}